package com.wk.database.entity;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;

import com.wk.database.entity.definition.Column;
import com.wk.database.entity.definition.ColumnType;
import com.wk.database.entity.definition.Condition;
import com.wk.database.entity.definition.Table;

/**
 * 方法类：查询实体类的全部实例
 * 
 * @author Bai Yu
 *
 * @param <Entity>
 */
@Deprecated
public class Query<Entity> {
	/**
	 * 创建查询方法<br>
	 * 如果成功创建，则可以预览对应的SQL语句
	 * 
	 * @param entity
	 *            实体类的类型。实体类满足：<br>
	 *            1.带有下列注解（annotation）：<br>
	 *            a.{@link Table}：指出对应的数据库表<br>
	 *            b.{@link Column}：指出各个字段对应的数据库列<br>
	 *            2.带有public的无参构造器<br>
	 *            3.当前不支持内部类
	 * @throws NoDefaultConstructorException
	 *             无法访问实体类的无参构造器
	 * @throws NoTableSpecifiedException
	 *             未指定数据库表，即实体类不带有{@link Table}注解
	 * @throws NoMappedFieldException
	 *             未指定要映射到数据库表的字段，即实体类的字段不带有{@link Column}
	 */
	public Query(Class<Entity> entity)
			throws NoDefaultConstructorException, NoTableSpecifiedException, NoMappedFieldException {
		this.entity = entity;

		constructor = EntityAnalyzer.findConstructor(entity);
		table = EntityAnalyzer.findTable(entity);
		mappedFields = EntityAnalyzer.findMappedFields(entity);
	}

	/**
	 * 预览将会创建的SQL查询语句。无副作用<br>
	 * 查询语句形如"select field1, field2 from table"<br>
	 * 其中，被查询的列按相应字段在实体类中出现的顺序排列
	 * 
	 * @return SQL语句
	 */
	public String getSQL() {
		StringBuilder result = new StringBuilder();
		result.append("select ");

		makeSelectClause(result);
		result.append(" from " + table);
		makeWhereClause(result);

		return result.toString();
	}

	/**
	 * 连接数据库<br>
	 * 如果连接成功，则可以任意次数地执行查询
	 * 
	 * @param connection
	 *            数据库连接
	 * @throws SQLException
	 *             连接失败
	 */
	public void connect(Connection connection) throws SQLException {
		statement = connection.prepareStatement(getSQL());
	}

	public List<Entity> execute() throws SQLException, NoDefaultConstructorException {
		List<Entity> results = new LinkedList<>();
		ResultSet rawResults = statement.executeQuery();

		while (rawResults.next()) {
			Entity result = makeEntity();
			fillFields(rawResults, result);

			results.add(result);
		}

		return results;
	}

	private void makeSelectClause(StringBuilder result) {
		boolean firstField = true;
		for (Field field : mappedFields) {
			Column column = field.getAnnotation(Column.class);

			if (firstField) {
				firstField = false;
				result.append(column.name());
			} else {
				result.append(", " + column.name());
			}
		}
	}

	/* 创建where子句，形如"where column1 > 5 and column2 = 'completed'" */
	private void makeWhereClause(StringBuilder result) {
		boolean firstCondition = true;
		for (Field field : mappedFields) {
			Condition condition = field.getAnnotation(Condition.class);
			if (condition == null) {
				continue;
			}

			Column column = field.getAnnotation(Column.class);

			if (firstCondition) {
				firstCondition = false;
				result.append(" where " + column.name() + " " + condition.value());
			} else {
				result.append(" and " + column.name() + " " + condition.value());
			}
		}
	}

	private Entity makeEntity() throws NoDefaultConstructorException {
		Entity result;
		try {
			result = constructor.newInstance();
		} catch (InstantiationException e) {
			throw new NoDefaultConstructorException(entity, e);
		} catch (IllegalAccessException e) {
			throw new NoDefaultConstructorException(entity, e);
		} catch (IllegalArgumentException e) {
			throw new NoDefaultConstructorException(entity, e);
		} catch (InvocationTargetException e) {
			throw new NoDefaultConstructorException(entity, e);
		}

		return result;
	}

	private void fillFields(ResultSet raw, Entity object) throws SQLException, NoDefaultConstructorException {
		int fieldIndex = 1;
		for (Field field : mappedFields) {
			String type = field.getAnnotation(Column.class).type();

			try {
				if (type.equals(ColumnType.STRING)) {
					field.set(object, raw.getString(fieldIndex));
				} else if (type.equals(ColumnType.INT)) {
					field.set(object, raw.getInt(fieldIndex));
				}
			} catch (IllegalArgumentException e) {
				throw new NoDefaultConstructorException(entity, e);
			} catch (IllegalAccessException e) {
				throw new NoDefaultConstructorException(entity, e);
			}

			fieldIndex++;
		}
	}

	// 实体类的类型
	private Class<Entity> entity;

	// 实体类的无参构造器，用于创建其实例作为查询结果
	private Constructor<Entity> constructor;

	// 映射到的数据库表
	private String table;

	// 映射到数据库列的字段。这些字段都有{@link Column}注解，并按实体类中的出现顺序排列
	private List<Field> mappedFields;

	private PreparedStatement statement;
}
